import json
import datetime
import requests
import copy

from rest_framework import exceptions

from system.services import organization_service
from datacenter.models import Entity
from system.services import encrypt_service
from libs.encrypt import sign, verify_signature, generate_open_id_and_second_key
from libs.auth.openapi import generate_openapi_headers
from connection.models import Certificate
from libs.enums import Authorizer, ConnectionState, PartnerType


class CertificateService:
    def get_certificate_by_open_id(self, open_id, is_voided=None):
        query = Certificate.objects.filter(holder_open_id=open_id)
        if is_voided is not None:
            query = query.filter(is_voided=is_voided)
        return query.first()

    def get_ongoing_certificates(self):
        queryset = Certificate.objects.filter(
            connection__state=ConnectionState.ONGOING.value,
            connection__partner__type=PartnerType.ORG.value,
            authorizer=Authorizer.OTHER_SIDE.value,
        ).select_related('connection', 'connection__partner')
        return queryset

    def check_certificate(self, certificate_data):
        self._check_certificate_struct(certificate_data)
        self._verify_signature(certificate_data)

    def _check_certificate_struct(self, certificate_data):
        must_keys = ['server', 'public_key', 'second_key', 'holder_open_id', 'signature', 'entities']
        for key in must_keys:
            if key in certificate_data:
                continue
            raise exceptions.ValidationError({'error': '授权书数据格式有误'})

    def _verify_signature(self, certificate_data):
        certificate_data_copy = copy.deepcopy(certificate_data)
        signature = certificate_data_copy.pop('signature', None)
        certificate_data_text = json.dumps(certificate_data_copy, indent=4, ensure_ascii=False)
        public_key_pem = certificate_data_copy.get('public_key')
        try:
            verify_signature(public_key_pem, signature, certificate_data_text)
        except Exception:
            raise exceptions.ValidationError({'error': '签名验证失败'})

    def get_server_and_headers(self, certificate_data):
        headers = generate_openapi_headers(certificate_data)
        return certificate_data['server'], headers

    def mask_certificate_data(self, certificate_data):
        certificate_data.pop('public_key', None)
        certificate_data.pop('second_key', None)
        certificate_data.pop('holder_open_id', None)
        return certificate_data

    def fill_entities(self, entities):
        if not entities:
            return []
        entity_keys = {e.get('key') for e in entities if e.get('key')}
        queryset = Entity.objects.filter(key__in=entity_keys)
        key_instance_map = {instance.key: instance for instance in queryset}
        entity_items = []
        for e in entities:
            key = e.get('key')
            fields = e.get('fields')
            if not key or not fields:
                continue
            instance = key_instance_map.get(key)
            if not instance:
                continue
            field_queryset = instance.fields.filter(field__in=fields)
            key_field_instance_map = {instance.field: instance for instance in field_queryset}
            field_items = []
            for field in fields:
                field_instance = key_field_instance_map.get(field)
                if not field_instance:
                    continue
                field_item = {
                    'field': field_instance.field,
                    'label': field_instance.label,
                    'standard_model_field': field_instance.standard_model_field,
                }
                field_items.append(field_item)
            entity_item = {
                'key': key,
                'name': instance.name,
                'source_db': instance.source_db,
                'fields': field_items,
                'primary_key': instance.primary_key,
                'ext_filter': e.get('ext_filter')
            }
            entity_items.append(entity_item)
        return entity_items

    def generate_certificate_data(self, connection, source_data, server):
        private_key, public_key, _ = encrypt_service.get_encrypt_key()
        system_organization = organization_service.get()
        holder_open_id, second_key = generate_open_id_and_second_key(private_key, connection.partner_id)
        certificate_data = {
            "holder": connection.partner.name,
            "provider": system_organization.name,
            "holder_open_id": holder_open_id,
            "provider_filings_id": system_organization.filings_id,
            "type": connection.partner.type,
            "public_key": public_key,
            "second_key": second_key,
            "server": server,
            "entities": self.fill_entities(source_data),
            "start_time": connection.start_time.strftime('%Y-%m-%d %H:%M:%S'),
            "end_time": connection.end_time.strftime('%Y-%m-%d %H:%M:%S'),
            "created_at": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        }
        certificate_data_text = json.dumps(certificate_data, indent=4, ensure_ascii=False)
        signature = sign(private_key, certificate_data_text)
        certificate_data['signature'] = signature
        return certificate_data

    def validate_certificate(self, certificate_data):
        ''' 检验授权书是否能够访问对方服务
        '''
        validated = False
        server, headers = self.get_server_and_headers(certificate_data)
        api = f'{server}/openapi/health/ping'
        try:
            response = requests.get(api, headers=headers)
            if response.status_code == 200:
                validated = True
        except Exception:
            pass
        return validated


certificate_service = CertificateService()
